package com.collins.thread.lock.cas;

import lombok.Data;
import lombok.ToString;

import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class AtomicReferenceTest {

    private static final int THREAD_CNT = 5;

    @Data
    @ToString
    private static class Student {
        public String name;
        public int age;
        public volatile int score;

        private static final AtomicIntegerFieldUpdater<Student> iUpdater =
                AtomicIntegerFieldUpdater.newUpdater(Student.class, "score");

        public void add(Student student, CountDownLatch latch) {
            int iter = 1000;
            System.out.println(Thread.currentThread().getName() + "comes in ...");
            try {
                for (int i = 0; i < iter; i++) {
                    iUpdater.getAndIncrement(student);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                latch.countDown();
            }
        }
    }

    private static class MyRef {

        private volatile Boolean isInit = false;

        private static final AtomicReferenceFieldUpdater<MyRef, Boolean> rUpdater =
                AtomicReferenceFieldUpdater.newUpdater(MyRef.class, Boolean.class, "isInit");

        public void init(MyRef ref) {
            if (rUpdater.compareAndSet(ref, Boolean.FALSE, Boolean.TRUE)) {
                System.out.println(Thread.currentThread().getName() + ": starts init ...");
                try {
                    TimeUnit.MILLISECONDS.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName() + ": successfully set init true");
            } else {
                System.out.println(Thread.currentThread().getName() + ": failed set init false");
            }
        }
    }

    public static void main(String[] args) {
        // AtomicReferenceTest test = new AtomicReferenceTest();
        Student student = new Student();
        MyRef ref = new MyRef();
        CountDownLatch latch = new CountDownLatch(THREAD_CNT);

        Scanner scanner = new Scanner(System.in);
        System.out.println("1 for atomic reference; 2 for atomic integer");
        int flag = 0;
        try {
            flag = scanner.nextInt();
        } catch (Exception e) {
            System.err.println("wrong input, please try again");
            System.exit(1);
        }
        switch (flag) {
            // TODO atomic reference - initialize
            case 1:
                for (int i = 0; i < THREAD_CNT; i++) {
                    new Thread(() -> {
                        ref.init(ref);
                    }).start();
                }
                break;
            // TODO atomic integer - add score
            case 2:
                student.setScore(0);
                for (int i = 0; i < THREAD_CNT; i++) {
                    new Thread(() -> {
                        student.add(student, latch);
                    }).start();
                }
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("all threads done... total results is: " + student.getScore());
                break;
            default:
                System.err.println("no such option");
                System.exit(1);
        }

    }

}



